package top.z.orm.handle.distribution.ds;

import top.z.orm.core.distribution.ds.Route;
import top.z.orm.core.osql.exception.NoConfigException;
import top.z.orm.handle.osql.core.HoneyConfig;
import top.z.orm.handle.osql.core.HoneyContext;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

public class OnlyMultipleDB implements Route {

    private static       String              defaultDs;
    private static final Map<String, String> entityClassPathToDs         = new ConcurrentHashMap<>();
    private static final Map<String, String> tableToDs                   = new ConcurrentHashMap<>();
    private static final List<String>        entityClassPathToDsWithStar = new CopyOnWriteArrayList<>();

    {
        //will run every time use new. for refresh.
        init();
    }

    private void init() {
        String matchEntityClassPath;
        String matchTable;
        defaultDs = HoneyConfig.getHoneyConfig().multiDsDefalutDS;

        //仅分库, 需要配置默认 DB
        if (defaultDs == null || "".equals(defaultDs.trim())) {
            throw new NoConfigException("Error: bee.dosql.multi-DS.defalut-DS can not null or empty when bee.dosql.multi-DS.type=2! ");
        }
        matchEntityClassPath = HoneyConfig.getHoneyConfig().matchEntityClassPath;
        matchTable = HoneyConfig.getHoneyConfig().matchTable;
        if ((matchEntityClassPath == null || "".equals(matchEntityClassPath.trim())) && (matchTable == null || "".equals(matchTable.trim()))) {
            throw new NoConfigException("Error: bee.dosql.multi-DS.match.entityClassPath and bee.dosql.multi-DS.match.table can not null or empty at same time when bee.dosql.multi-DS.type=2! ");
        }
        parseListToMap(matchEntityClassPath, entityClassPathToDs, true);
        //不带 * tableToDs 表名不区分大小
        parseListToMap(matchTable, tableToDs, false);
    }

    private static void parseListToMap(String str, Map<String, String> map, boolean isAdd2List) {
        // ds2:com.xxx.aa.User,com.xxx.bb.*,com.xxx.cc.**;ds3:com.xxx.dd.User
        if (str == null || "".equals(str.trim())) return;
        String[] str1 = str.split(";");
        for (String s : str1) {
            String[] str2 = s.split(":");
            String[] str3 = str2[1].trim().split(",");
            for (String value : str3) {
                if (isAdd2List && value.trim().endsWith(".**"))
                    // .** 结尾同时存一份到 list
                    entityClassPathToDsWithStar.add(value.trim());
                if (value.trim().indexOf(".") > 0) {
                    map.put(value.trim(), str2[0].trim());
                } else {
                    // 表名不区分大小写
                    map.put(value.trim().toLowerCase(), str2[0].trim());
                }

            }
        }
    }

    @Override
    public String getDsName() {
        RouteStruct routeStruct = HoneyContext.getCurrentRoute();
        if (routeStruct == null) return defaultDs;
        String   ds;
        String   tables = routeStruct.getTableNames();
        Class<?> clazz  = routeStruct.getEntityClass();
        if (clazz == null) {
            // 用 map 传递查询信息, 没有 JavaBean, 则 class = null. 但可以通过 bee.dosql.multi-DS.match.table 指定数据源
            ds = getDsViaTables(tables);
            if (ds != null) return ds;
            return defaultDs;
        }
        String fullName = clazz.getName();
        ds = entityClassPathToDs.get(fullName);
        if (ds != null) return ds;

        if (clazz.getPackage() != null) {
            String packageName = clazz.getPackage().getName();
            ds = entityClassPathToDs.get(packageName + ".*");
            if (ds != null) return ds;

            //ds=entityClassPathToDs.get(packageName+".**");   //com.xxx.** 省略多级情况下,不适用

            for (String s : entityClassPathToDsWithStar) {
                if (s.endsWith(".**")) {
                    String prePath = s.substring(0, s.length() - 2);
                    if (fullName.startsWith(prePath)) return entityClassPathToDs.get(s);
                }
//				else if (s.endsWith(".*")) {
//					String prePath = s.substring(0, s.length() - 1);
//					if (fullName.startsWith(prePath)) return entityClassPathToDs.get(s);
//				}
            }
            //TODO 为什么要在if (clazz == null)下进行??
            ds = getDsViaTables(tables);
            if (ds != null) return ds;
        }
        return defaultDs;
    }

    private String getDsViaTables(String tables) {
//		String tables = routeStruct.getTableNames();  //TODO 为什么要在if (clazz == null)下进行??
        String ds = null;
        if (tables != null) {
            if (!tables.contains("##")) {
                ds = tableToDs.get(tables.trim().toLowerCase());
                if (ds != null) return ds;
            } else { //only multi-Ds,tables don't allow in different db.仅分库时，多表查询的多个表要在同一个数据源.
                String ts[] = tables.split("##");
                ds = tableToDs.get(ts[0].toLowerCase());
                if (ds != null) return ds;
            }
        }
        return ds;
    }
}